import requests
import time
from typing import Dict, Any, List, Optional, Tuple
from utils.logger import get_logger
from utils.validators import Validators


class GiteeAPI:
    """Gitee API操作类"""
    
    def __init__(self, access_token: str, base_url: str = "https://gitee.com/api/v5"):
        self.access_token = access_token
        self.base_url = base_url
        self.session = requests.Session()
        self.logger = get_logger("GiteeAPI")
        
        # 设置请求头
        self.session.headers.update({
            'Content-Type': 'application/json',
            'User-Agent': 'GiteeRepoManager/1.0'
        })
    
    def test_connection(self) -> Tuple[bool, str]:
        """测试API连接和令牌有效性"""
        try:
            response = self.session.get(
                f"{self.base_url}/user",
                params={'access_token': self.access_token}
            )
            
            if response.status_code == 200:
                user_info = response.json()
                username = user_info.get('login', 'Unknown')
                self.logger.info(f"API连接测试成功，用户: {username}")
                return True, f"连接成功！当前用户: {username}"
            elif response.status_code == 401:
                return False, "访问令牌无效或已过期"
            else:
                return False, f"连接失败，状态码: {response.status_code}"
                
        except requests.exceptions.RequestException as e:
            self.logger.error(f"API连接测试失败: {e}")
            return False, f"网络连接错误: {str(e)}"
    
    def create_repository(self, repo_data: Dict[str, Any]) -> Tuple[bool, str, Optional[Dict]]:
        """
        创建仓库
        
        Args:
            repo_data: 仓库信息字典，包含以下字段:
                - name: 仓库名称
                - description: 仓库描述 (可选)
                - homepage: 项目主页 (可选) 
                - private: 是否私有 (默认False)
                - has_issues: 是否开启Issues (默认True)
                - has_wiki: 是否开启Wiki (默认True)
                - can_comment: 是否允许用户对仓库进行评论 (默认True)
                - auto_init: 是否自动初始化仓库 (默认False)
                - gitignore_template: gitignore模板 (可选)
                - license_template: 开源许可证模板 (可选)
        
        Returns:
            Tuple[bool, str, Optional[Dict]]: (成功状态, 消息, 仓库信息)
        """
        # 验证仓库名称
        is_valid, message = Validators.validate_repo_name(repo_data.get('name', ''))
        if not is_valid:
            return False, message, None
        
        # 构建请求数据
        is_private = repo_data.get('private', False)
        # Gitee API使用public参数: 0=私有, 1=外部开源, 2=内部开源
        public_value = 0 if is_private else 1
        
        data = {
            'access_token': self.access_token,
            'name': repo_data['name'],
            'description': repo_data.get('description', ''),
            'homepage': repo_data.get('homepage', ''),
            'public': public_value,  # 使用public参数而非private
            'has_issues': repo_data.get('has_issues', True),
            'has_wiki': repo_data.get('has_wiki', True),
            'can_comment': repo_data.get('can_comment', True),
            'auto_init': repo_data.get('auto_init', False)
        }
        
        # 记录调试信息
        visibility_text = "私有" if is_private else "公开"
        self.logger.info(f"创建仓库参数: public={public_value}({visibility_text}), name={repo_data['name']}, name_len={len(repo_data['name'])}")
        
        # 可选参数
        if repo_data.get('gitignore_template'):
            data['gitignore_template'] = repo_data['gitignore_template']
        if repo_data.get('license_template'):
            data['license_template'] = repo_data['license_template']
        
        try:
            response = self.session.post(f"{self.base_url}/user/repos", json=data)
            
            if response.status_code == 201:
                repo_info = response.json()
                self.logger.info(f"成功创建仓库: {repo_data['name']}")
                return True, f"仓库 '{repo_data['name']}' 创建成功", repo_info
            elif response.status_code == 422:
                try:
                    error_info = response.json()
                    error_msg = error_info.get('message', '仓库创建失败')
                    
                    # 处理常见错误信息
                    if ('name has already been taken' in str(error_info) or 
                        'already exists' in error_msg or 
                        '已存在同地址仓库' in str(error_info) or
                        'base' in error_info.get('error', {}) and '已存在' in str(error_info['error']['base'])):
                        return False, f"仓库名称 '{repo_data['name']}' 已存在", None
                    
                    # 输出详细错误信息用于调试
                    full_error = f"创建仓库失败: {error_msg}"
                    if 'errors' in error_info:
                        errors = error_info['errors']
                        if isinstance(errors, dict):
                            for field, msgs in errors.items():
                                full_error += f" | {field}: {msgs}"
                        elif isinstance(errors, list):
                            full_error += f" | 详细错误: {errors}"
                    
                    self.logger.error(f"仓库创建422错误: {error_info}")
                    return False, full_error, None
                    
                except Exception as e:
                    error_msg = f"创建仓库失败，状态码422，解析错误信息失败: {response.text}"
                    self.logger.error(error_msg)
                    return False, error_msg, None
            else:
                try:
                    error_info = response.json()
                    error_msg = error_info.get('message', '未知错误')
                    
                    # 详细错误信息
                    full_msg = f"创建仓库失败 (状态码: {response.status_code})"
                    
                    if error_msg != '未知错误':
                        full_msg += f": {error_msg}"
                    
                    # 检查是否有详细错误
                    if 'errors' in error_info:
                        errors = error_info['errors']
                        if isinstance(errors, dict):
                            for field, msgs in errors.items():
                                if isinstance(msgs, list):
                                    full_msg += f" | {field}: {', '.join(msgs)}"
                                else:
                                    full_msg += f" | {field}: {msgs}"
                    
                    # 特殊错误处理
                    if response.status_code == 400:
                        if len(repo_data['name']) > 30:
                            full_msg += " (可能是仓库名称过长)"
                    
                    self.logger.error(f"详细错误信息: {error_info}")
                    return False, full_msg, None
                    
                except Exception as parse_error:
                    error_msg = f"创建仓库失败，状态码: {response.status_code}, 响应解析失败: {response.text[:300]}"
                    self.logger.error(f"解析错误响应失败: {parse_error}")
                    return False, error_msg, None
                
        except requests.exceptions.RequestException as e:
            error_msg = f"网络请求失败: {str(e)}"
            self.logger.error(error_msg)
            return False, error_msg, None
    
    def get_user_repos(self, page: int = 1, per_page: int = 20) -> Tuple[bool, List[Dict], str]:
        """
        获取用户仓库列表
        
        Args:
            page: 页码
            per_page: 每页数量
            
        Returns:
            Tuple[bool, List[Dict], str]: (成功状态, 仓库列表, 消息)
        """
        try:
            params = {
                'access_token': self.access_token,
                'page': page,
                'per_page': per_page,
                'sort': 'updated',
                'direction': 'desc'
            }
            
            response = self.session.get(f"{self.base_url}/user/repos", params=params)
            
            if response.status_code == 200:
                repos = response.json()
                self.logger.info(f"成功获取仓库列表，共 {len(repos)} 个仓库")
                return True, repos, f"成功获取 {len(repos)} 个仓库"
            else:
                error_msg = f"获取仓库列表失败，状态码: {response.status_code}"
                self.logger.error(error_msg)
                return False, [], error_msg
                
        except requests.exceptions.RequestException as e:
            error_msg = f"网络请求失败: {str(e)}"
            self.logger.error(error_msg)
            return False, [], error_msg
    
    def get_all_user_repos(self, include_private: bool = True) -> Tuple[bool, List[Dict], str]:
        """
        获取用户的所有仓库（分页获取）
        
        Args:
            include_private: 是否包含私有仓库
            
        Returns:
            Tuple[bool, List[Dict], str]: (成功状态, 仓库列表, 消息)
        """
        try:
            all_repos = []
            page = 1
            per_page = 100  # 每页获取100个，减少请求次数
            
            while True:
                params = {
                    'access_token': self.access_token,
                    'page': page,
                    'per_page': per_page,
                    'sort': 'updated',
                    'direction': 'desc'
                }
                
                response = self.session.get(f"{self.base_url}/user/repos", params=params)
                
                if response.status_code != 200:
                    error_msg = f"获取仓库列表失败，状态码: {response.status_code}"
                    self.logger.error(error_msg)
                    return False, [], error_msg
                
                repos = response.json()
                if not repos:  # 没有更多仓库了
                    break
                
                # 过滤仓库
                for repo in repos:
                    if include_private or not repo.get('private', True):
                        all_repos.append(repo)
                
                self.logger.info(f"已获取第 {page} 页，本页 {len(repos)} 个仓库，累计 {len(all_repos)} 个仓库")
                
                # 如果返回的仓库数量少于每页数量，说明已经是最后一页
                if len(repos) < per_page:
                    break
                
                page += 1
                
                # 防止无限循环，最多获取50页
                if page > 50:
                    self.logger.warning("已达到最大页数限制（50页），停止获取")
                    break
            
            self.logger.info(f"成功获取所有仓库列表，共 {len(all_repos)} 个仓库")
            return True, all_repos, f"成功获取 {len(all_repos)} 个仓库"
                
        except requests.exceptions.RequestException as e:
            error_msg = f"网络请求失败: {str(e)}"
            self.logger.error(error_msg)
            return False, [], error_msg
    
    def delete_repository(self, repo_name: str) -> Tuple[bool, str]:
        """
        删除仓库
        
        Args:
            repo_name: 仓库名称
            
        Returns:
            Tuple[bool, str]: (成功状态, 消息)
        """
        try:
            # 先获取用户信息以获取用户名
            user_response = self.session.get(
                f"{self.base_url}/user",
                params={'access_token': self.access_token}
            )
            
            if user_response.status_code != 200:
                return False, "无法获取用户信息"
            
            username = user_response.json().get('login')
            
            # 删除仓库
            response = self.session.delete(
                f"{self.base_url}/repos/{username}/{repo_name}",
                params={'access_token': self.access_token}
            )
            
            if response.status_code == 204:
                self.logger.info(f"成功删除仓库: {repo_name}")
                return True, f"仓库 '{repo_name}' 删除成功"
            elif response.status_code == 404:
                return False, f"仓库 '{repo_name}' 不存在"
            else:
                error_msg = f"删除仓库失败，状态码: {response.status_code}"
                self.logger.error(error_msg)
                return False, error_msg
                
        except requests.exceptions.RequestException as e:
            error_msg = f"网络请求失败: {str(e)}"
            self.logger.error(error_msg)
            return False, error_msg
    
    def get_repo_info(self, repo_name: str) -> Tuple[bool, Optional[Dict], str]:
        """
        获取仓库信息
        
        Args:
            repo_name: 仓库名称
            
        Returns:
            Tuple[bool, Optional[Dict], str]: (成功状态, 仓库信息, 消息)
        """
        try:
            # 获取用户信息
            user_response = self.session.get(
                f"{self.base_url}/user",
                params={'access_token': self.access_token}
            )
            
            if user_response.status_code != 200:
                return False, None, "无法获取用户信息"
            
            username = user_response.json().get('login')
            
            # 获取仓库信息
            response = self.session.get(
                f"{self.base_url}/repos/{username}/{repo_name}",
                params={'access_token': self.access_token}
            )
            
            if response.status_code == 200:
                repo_info = response.json()
                return True, repo_info, "获取仓库信息成功"
            elif response.status_code == 404:
                return False, None, f"仓库 '{repo_name}' 不存在"
            else:
                error_msg = f"获取仓库信息失败，状态码: {response.status_code}"
                return False, None, error_msg
                
        except requests.exceptions.RequestException as e:
            error_msg = f"网络请求失败: {str(e)}"
            self.logger.error(error_msg)
            return False, None, error_msg
    
    def get_repository_info(self, repo_name: str) -> Tuple[bool, Optional[Dict]]:
        """
        获取仓库详细信息（兼容方法）
        
        Args:
            repo_name: 仓库名称
            
        Returns:
            Tuple[bool, Optional[Dict]]: (成功状态, 仓库信息)
        """
        success, repo_info, message = self.get_repo_info(repo_name)
        return success, repo_info
    
    def get_file_content(self, repo_full_name: str, file_path: str, branch: str = "master") -> Tuple[bool, str, str]:
        """
        获取仓库中指定文件的内容
        
        Args:
            repo_full_name: 仓库全名 (owner/repo)
            file_path: 文件路径
            branch: 分支名
            
        Returns:
            Tuple[bool, str, str]: (成功状态, 文件内容, 消息)
        """
        try:
            url = f"{self.base_url}/repos/{repo_full_name}/contents/{file_path}"
            params = {
                'access_token': self.access_token,
                'ref': branch
            }
            
            response = self.session.get(url, params=params, timeout=30)
            
            if response.status_code == 200:
                data = response.json()
                if data.get("type") == "file":
                    # 解码base64内容
                    import base64
                    content = base64.b64decode(data["content"]).decode('utf-8')
                    self.logger.info(f"成功获取文件内容: {file_path}")
                    return True, content, "获取文件内容成功"
                else:
                    return False, "", f"路径不是文件: {file_path}"
            elif response.status_code == 404:
                return False, "", f"文件不存在: {file_path}"
            else:
                error_msg = f"获取文件失败: {response.status_code} - {response.text}"
                self.logger.error(error_msg)
                return False, "", error_msg
                
        except Exception as e:
            error_msg = f"获取文件内容失败: {str(e)}"
            self.logger.error(error_msg)
            return False, "", error_msg
    
    def update_file_content(self, repo_full_name: str, file_path: str, content: str, 
                          commit_message: str, branch: str = "master") -> Tuple[bool, str]:
        """
        更新仓库中指定文件的内容
        
        Args:
            repo_full_name: 仓库全名 (owner/repo)
            file_path: 文件路径
            content: 新内容
            commit_message: 提交信息
            branch: 分支名
            
        Returns:
            Tuple[bool, str]: (成功状态, 消息)
        """
        try:
            # 先获取文件的sha值
            url = f"{self.base_url}/repos/{repo_full_name}/contents/{file_path}"
            params = {
                'access_token': self.access_token,
                'ref': branch
            }
            
            response = self.session.get(url, params=params, timeout=30)
            if response.status_code != 200:
                return False, f"无法获取文件信息: {response.status_code}"
            
            file_data = response.json()
            sha = file_data["sha"]
            
            # 更新文件
            import base64
            encoded_content = base64.b64encode(content.encode('utf-8')).decode('utf-8')
            
            data = {
                "message": commit_message,
                "content": encoded_content,
                "sha": sha,
                "branch": branch,
                "access_token": self.access_token
            }
            
            response = self.session.put(url, json=data, timeout=30)
            
            if response.status_code == 200:
                self.logger.info(f"成功更新文件: {file_path}")
                return True, f"成功更新文件: {file_path}"
            else:
                error_msg = f"更新文件失败: {response.status_code} - {response.text}"
                self.logger.error(error_msg)
                return False, error_msg
                
        except Exception as e:
            error_msg = f"更新文件内容失败: {str(e)}"
            self.logger.error(error_msg)
            return False, error_msg
    
    def get_file_content(self, repo_full_name: str, file_path: str, branch: str = "master") -> Tuple[bool, str, Optional[str]]:
        """
        获取仓库中指定文件的内容
        
        Args:
            repo_full_name: 仓库全名 (owner/repo)
            file_path: 文件路径
            branch: 分支名
            
        Returns:
            Tuple[bool, str, Optional[str]]: (成功状态, 消息, 文件内容Base64编码)
        """
        try:
            url = f"{self.base_url}/repos/{repo_full_name}/contents/{file_path}"
            params = {
                'access_token': self.access_token,
                'ref': branch
            }
            
            response = self.session.get(url, params=params, timeout=30)
            
            if response.status_code == 200:
                file_data = response.json()
                content = file_data.get("content", "")
                self.logger.info(f"成功获取文件: {file_path}")
                return True, f"成功获取文件: {file_path}", content
            elif response.status_code == 404:
                return False, f"文件不存在: {file_path}", None
            else:
                error_msg = f"获取文件失败: {response.status_code} - {response.text}"
                self.logger.error(error_msg)
                return False, error_msg, None
                
        except Exception as e:
            error_msg = f"获取文件失败: {str(e)}"
            self.logger.error(error_msg)
            return False, error_msg, None